#include "sliceutils.hpp"

namespace SliceUtils {
  void invert(unsigned char &value) {
    value=value==0?1:0;
  }

  void sphereMask(unsigned char &src,unsigned char &dest,const Vector3i &index,void *param) {
    SphereParam *mask=static_cast<SphereParam *>(param);
    if (dest==0 and src!=0) {
      if ((mask->centre-(index.cast<double>()+Vector3d(0.5,0.5,0.5))).norm()<=mask->radius) {
        dest=1;
      }
      else dest=0;
    }
  }

  void sum(unsigned char &value,void *param) {
    *((int *)param)+=value;
  }

  void cog(unsigned char &value,const Vector3i &index,void *param) {
    CoGParam *cogParam=static_cast<CoGParam *>(param);
    if (value!=0) {
      cogParam->num++;
      cogParam->accumulator+=index;
    }
  }

  void label(Slice &input,Slice &labelled,void *param) {
    LabelParam *labelParam=static_cast<LabelParam *>(param);
    if (input.value()!=0) {
      labelParam->num++;
      labelParam->indices.append(input.getIndex());
      labelled.value(input.getIndex())=labelParam->label;
      while (labelParam->indices.size()>0) {
        Vector3i index=labelParam->indices.takeLast();
        input.value(index)=0;
        Slice neighbours=input.slice(index-Vector3i(1,1,1),index+Vector3i(2,2,2));
        SliceIterator it(neighbours);
        if (labelParam->offsets.size()>0) {
          it.setIndices(labelParam->offsets,index);
        }
        while (it.hasNext()) {
          it.next();
          if (it.value()!=0) {
            labelParam->indices.append(it.getIndex());
            labelled.value(it.getIndex())=labelParam->label;
          }
        }
      }
      labelParam->label++;
    }
  }

  void boundingBox(unsigned char &value,const Vector3i &index,void *param) {
    BoundingBoxParam *bbParam=static_cast<BoundingBoxParam *>(param);
    if (value==bbParam->label) {
      bbParam->min=bbParam->min.cwiseMin(index);
      bbParam->max=bbParam->max.cwiseMax(index+Vector3i(1,1,1));
    }
  }

  void grow(Slice &region,Slice &grown,Slice &object) {
    if (region.value()==0 and object.value()!=0) {
      Slice neighbours=region.slice(region.getIndex()-Vector3i(1,1,1),region.getIndex()+Vector3i(2,2,2));
      SliceIterator it(neighbours);
      int s=0;
      it.execute(sum,&s);
      if (s>0) {
        grown.value()=1;
      }
    }
  }

  void unset(unsigned char &value,unsigned char &mask) {
    if (mask!=0) {
      value=0;
    }
  }

  void filter(unsigned char &src,unsigned char &dest,void *param) {
    FilterParam *filterParam=static_cast<FilterParam *>(param);
    if (src==filterParam->srcValue) {
      dest=filterParam->destValue;
    }
  }

  void maxDistanceToPoint(unsigned char &value,const Vector3i &index,void *param) {
    MaxDistanceParam *mdParam=static_cast<MaxDistanceParam *>(param);
    if (value!=0) {
      double dist=(Vector3d(0.5,0.5,0.5)+index.cast<double>()-mdParam->point).norm();
      if (dist>mdParam->distance) {
        mdParam->distance=dist;
      }
    }
  }
}
